
from game.object.world.block import *
from game.object.world.world import *

#a cell is a representation of a discrete block of land

CELL_STATE_EMPTY = 0
CELL_STATE_LOADING = 1
CELL_STATE_VALID = 2

CELL_WIDTH = 10;  # X
CELL_HEIGHT = 10; # Y
CELL_DEPTH = 10;  # Z


#A cell also holds references to cells on each face, like a linked list
#To visualise the reference, the world coordinates are aligned so that
#X+ is to the right, Y+ is to the top, and Z+ is out of the screen (RHR)
#This is denoted as xp, xn, yp, yn, zp, zn
class Cell( Block ):
    def __init__(self, depth, parent,**args):
        Block.__init__(self,**args)
        args.setdefault("xp",None)
        args.setdefault("xn",None)
        args.setdefault("yp",None)
        args.setdefault("yn",None)
        args.setdefault("zp",None)
        args.setdefault("zn",None)
        args.setdefault("ter",None)

        self.depth = depth;
        self.parent = parent;

        self.neigbours = (args["xp"],args["xn"],args["yp"],args["yn"],args["zp"],args["zn"])

        #cells are streamed into the game, so a cell is initially created in an
        #empty state. It is the responsibility of the engine to load the cell
        #with data and change it to a valid state
        #the states:
        #empty: The cell has been created, but no data is entered
        #loading: The data in the cell is being entered, but has not finished
        #valid: The data is ready for reading
        #writing: The data is being written to the datasource
        #clear: The cell is ready to be deleted
        self.state = 'empty'

        self.first_child = None
        self.last_child = None

        #an array of blocks, generated by the engine is stored here
        self.blocks = [None]*(CELL_WIDTH*CELL_HEIGHT*CELL_DEPTH)

        self.count = 0

        self.entities = []

    def get_block(self, x, y, z):
        """
        Get a block in the cell at the given coordinates, if the cell index is out of the
        scope of this cell this method will attempt to get the heigbouring cell
        and get the block from it using the normalise_block_loc() method
        """

        cell, x, y, z = self.normalise_block_loc(x,y,z)

        if(cell == None):
            return None;


        rcell = cell.blocks[x+(y*CELL_WIDTH)+(z*CELL_WIDTH*CELL_HEIGHT)]
        if(rcell is None):
            rcell = Cell(self.depth+1, self.parent)
            #print "Adding cell ", rcell, " to ", self, " xyz ", x, y, z, " self.xyz ", self.z, self.y, self.z, " self.count ", self.count, "self.depth", self.depth
            cell.set_block(x,y,z,rcell)

        return rcell;

    def normalise_block_loc(self,x,y,z):
        """
        Get's the actual location of a block assuming that x,y and z might infact
        be outside of the scope of this cell. This method will return a tuple containing
        the actual cell the block should be in and the x,z and y location the block
        be within that cell
        """

        cell = self;
        #Check all x, y and z are within the bounds of the block
        x_in_valid = True
        y_in_valid = True
        z_in_valid = True
        change = True
        while (change and (x_in_valid or y_in_valid or z_in_valid)):
            change = False
            if( x < 0 ):
                tcell = cell.get_neighbour(WORLD_XN)
                if(tcell != None):
                    x += CELL_WIDTH
                    cell = tcell
                    change = True
            elif( x > CELL_WIDTH):
                tcell = cell.get_neighbour(WORLD_XP)
                if(tcell != null):
                    x -= CELL_WIDTH
                    cell = tcell
                    change = True
            else:
                x_in_valid = False
            if( y < 0 ):
                tcell = cell.get_neighbour(WORLD_YN)
                if(tcell != None):
                    y += CELL_HEIGHT
                    cell = tcell
                    change = True
            elif( y > CELL_HEIGHT):
                tcell = cell.get_neighbour(WORLD_YP)
                if(tcell != null):
                    y -= CELL_HEIGHT
                    cell = tcell
                    change = True
            else:
                y_in_valid = False

            if( z < 0 ):
                tcell = cell.get_neighbour(WORLD_ZN)
                if(tcell != None):
                    z += CELL_DEPTH
                    cell = tcell
                    change = True
            elif( z > CELL_DEPTH):
                tcell = cell.get_neighbour(WORLD_ZP)
                if(tcell != null):
                    z -= CELL_DEPTH
                    cell = tcell
                    change = True
            else:
                z_in_valid = False

        # There are two possible states at this stage, we have found the correct
        # cell (could be this cell, and will be most of the time) and the x,y and z
        # are correct for this cell. OR we could not find the correct cell
        # in which case we will return null now... renderers beware =]

        if(x_in_valid or y_in_valid or z_in_valid):
            return (None,None,None,None)
        return (cell, x,y,z)

    def set_block(self,x,y,z,block):
        """
        Set a block in the cell at the given coordinates, if the cell index is out of the
        scope of this cell this method will attempt to get the heigbouring cell
        and get the block from it using the normalise_block_loc() method
        @returns true if the block was set and false if setting the block failed
        """

        #debuging, only add to this cell
        cell = self
        #if(x < 0 or x < 10 or y > 0 or y < 10 or z > 0 or z < 10):

        #cell, x, z, y = self.normalise_block_loc(x,y,z)

        if(cell == None):
            return False;

        block.x = x
        block.y = y
        block.z = z

        eb = cell.blocks[x+(y*CELL_WIDTH)+(z*CELL_WIDTH*CELL_HEIGHT)]

        if(eb != None):
            if(eb.next != None):
                eb.next.prev = block
                block.next = eb.next
            else:
                eb.parent.last_child

            if(eb.prev != None):
                eb.prev.next = block
                block.prev = eb.prev
            else:
                eb.parent.first_child = block
            block.parent = cell
            
            print "Block already set - replaced"


        cell.blocks[x+(y*CELL_WIDTH)+(z*CELL_WIDTH*CELL_HEIGHT)] = block

        if(eb is None):
            cell.add_child(block)
        
        return True;

    def register_block(self,x,y,z,block):
        register_neigbour(self, block, x+1, y, z, WORLD_XP, WORLD_XN)
        register_neigbour(self, block, x-1, y, z, WORLD_XN, WORLD_XP)
        register_neigbour(self, block, x, y+1, z, WORLD_YP, WORLD_YN)
        register_neigbour(self, block, x, y-1, z, WORLD_YN, WORLD_YP)
        register_neigbour(self, block, x, y, z+1, WORLD_ZP, WORLD_ZN)
        register_neigbour(self, block, x, y, z-1, WORLD_ZN, WORLD_ZP)


    def register_neigbour(self, block, x, y, z, dir, dir2):
        cell = get_block(self, x, y, z)
        if(cell != null):
            block.neigbours[dir] = cell
            cell.neigbours[dir2] = block

    def add_child(self,block):
        self.count += 1
        block.parent = self

        if(self.first_child is None):
            self.first_child = block;
            self.last_child = block;
            return

        block.prev = self.last_child
        self.last_child.next = block
        self.last_child = block;

    def get_neighbour(self, direction):
        """
        This function will return the neighbouring cell in the given direction
        the direction should be an int value from 0 - 5 as defined by the WORLD_x
        constaints in the world module
        """
        print "Checking for existing neighbour"
        cell = self.neigbours[direction]
        if(cell is None):
            print "No neighour in this direction, create one"
            cell = Cell(self.depth, self.parent)
            self.parent.set_block(self.x + WORLD_X[direction], self.y + WORLD_Y[direction], self.y + WORLD_Y[direction], block)
        return self.neigbours[direction]

#    def get_neighour_xyz(self,direction):
#        return self.x + WORLD_X[direction], self.y + WORLD_Y[direction],  self.y + WORLD_Y[direction]

    #def get_child_cell(self, coord, coord_depth):

    def load_from_string(self):
        """
        Loads a cell from a string, this assumes that all sub blocks are acutally
        blocks not cells
        """

